namespace GYLite
{
    export class Vector3{
        public elements:Float32Array;
        constructor(x:number=0, y:number=0, z:number=0) {
            this.elements = new Float32Array(3);            
            var v = this.elements;
            v[0] = x;
            v[1] = y;
            v[2] = z;
        }

        /**
         *从Array数组拷贝值。
         *@param array 数组。
         *@param offset 数组偏移。
         */
        public fromArray(array:number[], offset:number=0):void {            
            this.elements[0] = array[offset + 0];
            this.elements[1] = array[offset + 1];
            this.elements[2] = array[offset + 2];
        }

        /**
         *克隆。
         *@param destObject 克隆源。
         */
        public cloneTo(destObject:Vector3):void {
            var destVector3 = destObject;
            var destE = destVector3.elements;
            var s = this.elements;
            destE[0] = s[0];
            destE[1] = s[1];
            destE[2] = s[2];
        }

        /**
         *克隆。
         *@return 克隆副本。
         */
        public clone():Vector3 {      
            let cls:any;
            cls = this.constructor;
            var destVector3:Vector3 = new cls();
            this.cloneTo(destVector3);
            return destVector3;
        }

        public toDefault():void {
            this.elements[0] = 0;
            this.elements[1] = 0;
            this.elements[2] = 0;
        }

        public reset(x:number, y:number, z:number):void {
            this.elements[0] = x;
            this.elements[1] = y;
            this.elements[2] = z;
        }

        /**
         *设置X轴坐标。
         *@param value X轴坐标。
         */
        public set x(value:number) {
            this.elements[0] = value;
        }
        /**
         *获取X轴坐标。
         *@return X轴坐标。
         */
        public get x():number {
            return this.elements[0];
        }
        

        /**
         *设置Y轴坐标。
         *@param value Y轴坐标。
         */
        /**
         *获取Y轴坐标。
         *@return Y轴坐标。
         */
        public get y():number {
            return this.elements[1];
        }
        public set y(value:number) {
            this.elements[1] = value;
        }

        /**
         *设置Z轴坐标。
         *@param value Z轴坐标。
         */
        public set z(value:number) {
            this.elements[2] = value;
        }
        /**
         *获取Z轴坐标。
         *@return Z轴坐标。
         */
        public get z():number {
            return this.elements[2];
        }
        

        public static distanceSquared(value1:Vector3, value2:Vector3):number {
            var value1e:Float32Array = value1.elements;
            var value2e:Float32Array = value2.elements;
            var x = value1e[0] - value2e[0];
            var y = value1e[1] - value2e[1];
            var z = value1e[2] - value2e[2];
            return (x * x) + (y * y) + (z * z);
        }

        public static distance(value1:Vector3, value2:Vector3):number {
            var value1e:Float32Array = value1.elements;
            var value2e:Float32Array = value2.elements;
            var x = value1e[0] - value2e[0];
            var y = value1e[1] - value2e[1];
            var z = value1e[2] - value2e[2];
            return Math.sqrt((x * x) + (y * y) + (z * z));
        }

        public static min(a:Vector3, b:Vector3, out:Vector3):void {
            var e:Float32Array = out.elements;
            var f:Float32Array = a.elements;
            var g:Float32Array = b.elements
            e[0] = Math.min(f[0], g[0]);
            e[1] = Math.min(f[1], g[1]);
            e[2] = Math.min(f[2], g[2]);
        }

        public static max(a:Vector3, b:Vector3, out:Vector3):void {
            var e:Float32Array = out.elements;
            var f:Float32Array = a.elements;
            var g:Float32Array = b.elements
            e[0] = Math.max(f[0], g[0]);
            e[1] = Math.max(f[1], g[1]);
            e[2] = Math.max(f[2], g[2]);
        }

        public static transformQuat(source:Vector3, rotation:Quaternion, out:Vector3):void {
            var destination:Float32Array = out.elements;
            var se:Float32Array = source.elements;
            var re:Float32Array = rotation.elements;
            var x = se[0], y = se[1], z = se[2], qx = re[0], qy = re[1], qz = re[2], qw = re[3],
                ix = qw * x + qy * z - qz * y, iy = qw * y + qz * x - qx * z, iz = qw * z + qx * y - qy * x,
                iw = -qx * x - qy * y - qz * z;
            destination[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
            destination[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
            destination[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
        }

        public static scalarLength(a:Vector3):number {
            var f:Float32Array = a.elements;
            var x:number = f[0], y:number = f[1], z:number = f[2];
            return Math.sqrt(x * x + y * y + z * z);
        }

        public static scalarLengthSquared(a:Vector3):number {
            var f:Float32Array = a.elements;
            var x:number = f[0], y:number = f[1], z:number = f[2];
            return x * x + y * y + z * z;
        }

        public static normalize(s:Vector3, out:Vector3):void {
            var se:Float32Array = s.elements;
            var oe:Float32Array = out.elements;
            var x:number = se[0], y:number = se[1], z:number = se[2];
            var len = x * x + y * y + z * z;
            if (len > 0) {
                len = 1 / Math.sqrt(len);
                oe[0] = se[0] * len;
                oe[1] = se[1] * len;
                oe[2] = se[2] * len;
            }
        }

        public static multiply(a:Vector3, b:Vector3, out:Vector3):void {
            var e:Float32Array = out.elements;
            var f:Float32Array = a.elements;
            var g:Float32Array = b.elements
            e[0] = f[0] * g[0];
            e[1] = f[1] * g[1];
            e[2] = f[2] * g[2];
        }

        public static scale(a:Vector3, b:number, out:Vector3):void {
            var e:Float32Array = out.elements;
            var f:Float32Array = a.elements;
            e[0] = f[0] * b;
            e[1] = f[1] * b;
            e[2] = f[2] * b;
        }

        public static lerp(a:Vector3, b:Vector3, t:number, out:Vector3):void {
            var e:Float32Array = out.elements;
            var f:Float32Array = a.elements;
            var g:Float32Array = b.elements;
            var ax = f[0], ay = f[1], az = f[2];
            e[0] = ax + t * (g[0] - ax);
            e[1] = ay + t * (g[1] - ay);
            e[2] = az + t * (g[2] - az);
        }

        public static transformV3ToV3(vector:Vector3, transform:Matrix4x4, result:Vector3):void {
            var intermediate:Vector4 = Vector3._tempVector4;
            Vector3.transformV3ToV4(vector, transform, intermediate);
            var intermediateElem:Float32Array = intermediate.elements;
            var resultElem:Float32Array = result.elements;
            resultElem[0] = intermediateElem[0];
            resultElem[1] = intermediateElem[1];
            resultElem[2] = intermediateElem[2];
        }

        public static transformV3ToV4(vector:Vector3, transform:Matrix4x4, result:Vector4):void {
            var vectorElem:Float32Array = vector.elements;
            var vectorX:number = vectorElem[0];
            var vectorY:number = vectorElem[1];
            var vectorZ:number = vectorElem[2];
            var transformElem:Float32Array = transform.elements;
            var resultElem:Float32Array = result.elements;
            resultElem[0] = (vectorX * transformElem[0]) + (vectorY * transformElem[4]) + (vectorZ * transformElem[8]) + transformElem[12];
            resultElem[1] = (vectorX * transformElem[1]) + (vectorY * transformElem[5]) + (vectorZ * transformElem[9]) + transformElem[13];
            resultElem[2] = (vectorX * transformElem[2]) + (vectorY * transformElem[6]) + (vectorZ * transformElem[10]) + transformElem[14];
            resultElem[3] = (vectorX * transformElem[3]) + (vectorY * transformElem[7]) + (vectorZ * transformElem[11]) + transformElem[15];
        }

        public static TransformNormal(normal:Vector3, transform:Matrix4x4, result:Vector3):void {
            var normalElem:Float32Array = normal.elements;
            var normalX:number = normalElem[0];
            var normalY:number = normalElem[1];
            var normalZ:number = normalElem[2];
            var transformElem:Float32Array = transform.elements;
            var resultElem:Float32Array = result.elements;
            resultElem[0] = (normalX * transformElem[0]) + (normalY * transformElem[4]) + (normalZ * transformElem[8]);
            resultElem[1] = (normalX * transformElem[1]) + (normalY * transformElem[5]) + (normalZ * transformElem[9]);
            resultElem[2] = (normalX * transformElem[2]) + (normalY * transformElem[6]) + (normalZ * transformElem[10]);
        }

        public static transformCoordinate(coordinate:Vector3, transform:Matrix4x4, result:Vector3):void {
            var vectorElem:Float32Array = Vector3._tempVector4.elements;
            var coordinateElem:Float32Array = coordinate.elements;
            var coordinateX:number = coordinateElem[0];
            var coordinateY:number = coordinateElem[1];
            var coordinateZ:number = coordinateElem[2];
            var transformElem:Float32Array = transform.elements;
            vectorElem[0] = (coordinateX * transformElem[0]) + (coordinateY * transformElem[4]) + (coordinateZ * transformElem[8]) + transformElem[12];
            vectorElem[1] = (coordinateX * transformElem[1]) + (coordinateY * transformElem[5]) + (coordinateZ * transformElem[9]) + transformElem[13];
            vectorElem[2] = (coordinateX * transformElem[2]) + (coordinateY * transformElem[6]) + (coordinateZ * transformElem[10]) + transformElem[14];
            vectorElem[3] = 1.0 / ((coordinateX * transformElem[3]) + (coordinateY * transformElem[7]) + (coordinateZ * transformElem[11]) + transformElem[15]);
            var resultElem:Float32Array = result.elements;
            resultElem[0] = vectorElem[0] * vectorElem[3];
            resultElem[1] = vectorElem[1] * vectorElem[3];
            resultElem[2] = vectorElem[2] * vectorElem[3];
        }

        public static Clamp(value:Vector3, min:Vector3, max:Vector3, out:Vector3):void {
            var valuee:Float32Array = value.elements;
            var x:number = valuee[0];
            var y:number = valuee[1];
            var z:number = valuee[2];
            var mine:Float32Array = min.elements;
            var mineX:number = mine[0];
            var mineY:number = mine[1];
            var mineZ:number = mine[2];
            var maxe:Float32Array = max.elements;
            var maxeX:number = maxe[0];
            var maxeY:number = maxe[1];
            var maxeZ:number = maxe[2];
            var oute = out.elements;
            x = (x > maxeX) ? maxeX : x;
            x = (x < mineX) ? mineX : x;
            y = (y > maxeY) ? maxeY : y;
            y = (y < mineY) ? mineY : y;
            z = (z > maxeZ) ? maxeZ : z;
            z = (z < mineZ) ? mineZ : z;
            oute[0] = x;
            oute[1] = y;
            oute[2] = z;
        }

        public static add(a:Vector3, b:Vector3, out:Vector3):void {
            var e:Float32Array = out.elements;
            var f:Float32Array = a.elements;
            var g:Float32Array = b.elements
            e[0] = f[0] + g[0];
            e[1] = f[1] + g[1];
            e[2] = f[2] + g[2];
        }

        public static subtract(a:Vector3, b:Vector3, o:Vector3):void {
            var oe:Float32Array = o.elements;
            var ae:Float32Array = a.elements;
            var be:Float32Array = b.elements;
            oe[0] = ae[0] - be[0];
            oe[1] = ae[1] - be[1];
            oe[2] = ae[2] - be[2];
        }

        public static cross(a:Vector3, b:Vector3, o:Vector3):void {
            var ae:Float32Array = a.elements;
            var be:Float32Array = b.elements;
            var oe:Float32Array = o.elements;
            var ax:number = ae[0], ay:number = ae[1], az:number = ae[2], bx:number = be[0], by:number = be[1], bz:number = be[2];
            oe[0] = ay * bz - az * by;
            oe[1] = az * bx - ax * bz;
            oe[2] = ax * by - ay * bx;
        }

        public static dot(a:Vector3, b:Vector3):number {
            var ae:Float32Array = a.elements;
            var be:Float32Array = b.elements;
            var r:number = (ae[0] * be[0]) + (ae[1] * be[1]) + (ae[2] * be[2]);
            return r;
        }

        public static equals(a:Vector3, b:Vector3):boolean {
            var ae:Float32Array = a.elements;
            var be:Float32Array = b.elements;
            return MathUtils3D.nearEqual(Math.abs(ae[0]), Math.abs(be[0])) && MathUtils3D.nearEqual(Math.abs(ae[1]), Math.abs(be[1])) && MathUtils3D.nearEqual(Math.abs(ae[2]), Math.abs(be[2]));
        }

        public static ZERO = new Vector3(0.0, 0.0, 0.0);
        public static ONE = new Vector3(1.0, 1.0, 1.0);
        public static NegativeUnitX = new Vector3(-1, 0, 0);
        public static UnitX = new Vector3(1, 0, 0);
        public static UnitY = new Vector3(0, 1, 0);
        public static UnitZ = new Vector3(0, 0, 1);
        public static ForwardRH = new Vector3(0, 0, -1);
        public static ForwardLH = new Vector3(0, 0, 1);
        public static Up = new Vector3(0, 1, 0);
        public static NAN = new Vector3(NaN, NaN, NaN);
        public static _tempVector4 = new Vector4();        
        
    }
}